/*
SoftSerial.cpp
Description:    Library to enable software based serial
                communication using interrupts

Author:         T Bristow

Date Created:   20/04/13 
Date Modified:  21/04/13
Revision:       2
*/

#include "SoftSerial.h"

void (_SoftSerial::*_t1ISR)();
void (_SoftSerial::*_i0ISR)();

void _SoftSerial::begin(int baud){
  _ptrRead=0;
  _ptrWrite=0;
  pinMode(PIN2,INPUT);

  _t1ISR=&_SoftSerial::_Timer1InterruptRoutine;
  _i0ISR=&_SoftSerial::_Int0InterruptRoutine;

  _baud=2000000/baud;    //Calculate baud (based on 16MHz processor)

  SREG|=B10000000;       //Global interrupt enable

  // Setup timer1 to interrupt on overflow, internal clock, 1:8 prescaler
  TCCR1A=0;
  TCCR1B=B00000010;
  TIMSK1=B00000000;

  //External Interrupt enable on pin2 falling edge
  EICRA=B00000010;
  EIMSK=B00000001;
}

void _SoftSerial::_Timer1InterruptRoutine(){
  // reset timer to trigger in next bit
  TCNT1=0xFFFF-_baud;
  //if less than eight bytes have been written
  if(_i<8){
    _b|=digitalRead(PIN2)<<_i;  //read the pin and add it to the temp byte
    _i++;
  }
  else{  //when all bytes have been received
    buf[_ptrWrite++]=_b;       //Add the temp byte to the buffer
    _ptrWrite%=BUFFER_SIZE;
    if(_ptrWrite==_ptrRead)
      _ptrRead++;
    TIMSK1=B00000000;          //Switch off the timer
    EIMSK=B00000001;           //Re-enable the falling edge interrupt
    EIFR=B00000011;
  }
}


ISR(TIMER1_OVF_vect){
  (SoftSerial.*_t1ISR)();
}

//Fired when falling edge detected on pin 2
void _SoftSerial::_Int0InterruptRoutine(){
  //reset temporary storage variables
  _b=0;
  _i=0;

  //Disable external interrupt for now
  EIMSK=B00000000;

  //Enable timer and set it to overflow in 1.5*baud ticks, in middle of bit0
  TIFR1=B00000111;
  TIMSK1=B00000001;
  TCNT1=0xFFFF-_baud;
}

ISR(INT0_vect){
  (SoftSerial.*_i0ISR)();
}

// Read a byte from the buffer and increment the pointer
int _SoftSerial::read(void){
  byte b;
  if(available()){
    b=buf[_ptrRead++];
    _ptrRead%=BUFFER_SIZE;
    return b;
  }
  else
    return -1;
}

int _SoftSerial::peek(void){
  byte b;
  if(available()){
    b=buf[_ptrRead];
    return b;
  }
  else
    return -1;
}

// Check if there are bytes available to read
int _SoftSerial::available(void){
  if(_ptrWrite<_ptrRead)
    return _ptrWrite+BUFFER_SIZE-_ptrRead;
  else
    return _ptrWrite-_ptrRead;
}

void _SoftSerial::clear(void){
  _ptrWrite=0;
  _ptrRead=0;
}

_SoftSerial SoftSerial;

